﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Net;
using System.Net.Sockets;
using System.Threading;

namespace WebParser
{
    public abstract class LoopBased
    {
        public event CancelEventHandler Starting;
        public event CancelEventHandler Stopping;
        public event EventHandler       Started ;
        public event EventHandler       Stopped ;

        protected virtual void OnStarting   (CancelEventArgs e  ) { if (Starting  != null) Starting  (this, e); }
        protected virtual void OnStopping   (CancelEventArgs e  ) { if (Stopping  != null) Stopping  (this, e); }
        protected virtual void OnStarted    (EventArgs e        ) { if (Started   != null) Started   (this, e); }
        protected virtual void OnStopped    (EventArgs e        ) { if (Stopped   != null) Stopped   (this, e); }

        private readonly List<object> _sync;
        protected bool GetSync(object token)
        {
            return _sync.Contains(token);
        }
        protected void SetSync(object token, bool enable)
        {
            var contains = _sync.Contains(token);
            if ((enable && contains) || (!enable && !contains)) return;
            if (enable)
                _sync.Add(token);
            else
                _sync.Remove(token);
        }
        public bool IsRunning
        {
            get { return GetSync("IsRunning"); }
            private set { SetSync("IsRunning", value); }
        }
        protected bool KeepRunning
        {
            get { return GetSync("KeepRunning"); }
            set { SetSync("KeepRunning", value); }
        }

        protected LoopBased()
        {
            _sync = new List<object>();
        }
        public bool Start()
        {
            if (IsRunning) return false;
            IsRunning = true;
            var e = new CancelEventArgs(false);
            OnStarting(e);
            if (e.Cancel) return false;
            if (!BeforeStart()) return false;
            new Thread(ExecuteLoopJob) { IsBackground = true }.Start();
            OnStarted(EventArgs.Empty);
            return true;
        }

        protected abstract bool BeforeStart();
        protected abstract void AfterStart();

        protected abstract void LoopJob();

        protected abstract bool BeforeStop();
        protected abstract void AfterStop();

        private void ExecuteLoopJob()
        {
            AfterStart();
            KeepRunning = true;
            while (KeepRunning)
            {
                LoopJob();
            }
            AfterStop();
            IsRunning = false;
        }
        public bool Stop()
        {
            if (!IsRunning) return false;
            var e = new CancelEventArgs(false);
            OnStopping(e);
            if (e.Cancel) return false;
            if (!BeforeStop()) return false;
            KeepRunning = false;
            return true;
        }
    }

    public class ClientEventArgs : EventArgs
    {
        public Socket Client { get; private set; }
        public ClientEventArgs(Socket client)
        {
            Client = client;
        }
    }
    public class TcpServer : LoopBased
    {
        public event EventHandler<ClientEventArgs> ClientConnected;
        protected virtual void OnClientConnected(ClientEventArgs e)
        {
            if (ClientConnected != null) ClientConnected(this, e);
        }

        public event EventHandler<ErrorEventArgs> Error;
        protected virtual  void OnError(ErrorEventArgs e)
        {
            if (Error != null) Error(this, e);
        }

        private int _port;
        public int Port
        {
            get { return _port; }
            set
            {
                if (_port == value) return;
                if (value < 0 || value > 65535)
                    throw new ArgumentOutOfRangeException("Port");
                _port = value;
            }
        }

        private Socket _socket;

        public TcpServer()
            : this(0)
        {}
        public TcpServer(int port)
        {
            Port = port;
        }

        protected override bool BeforeStart()
        {
            return true;
        }

        protected override void AfterStart()
        {
        }

        protected override void LoopJob()
        {
            try
            {
                _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
                _socket.Bind(new IPEndPoint(IPAddress.Any, Port));
                _socket.Listen(16);
                while (KeepRunning)
                {
                    Socket client;
                    try
                    {
                        client = _socket.Accept();
                    }
                    catch (SocketException ex)
                    {
                        OnError(new ErrorEventArgs(ex));
                        break;
                    }
                    try
                    {
                        OnClientConnected(new ClientEventArgs(client));
                    }
                    catch (Exception ex)
                    {
                        OnError(new ErrorEventArgs(ex));
                    }
                }
            }
            catch (SocketException ex)
            {
                if (KeepRunning)
                    OnError(new ErrorEventArgs(ex));
            }
            catch (Exception ex)
            {
                OnError(new ErrorEventArgs(ex));
            }
        }

        protected override bool BeforeStop()
        {
            try
            {
                _socket.Close(1000);
            }
            catch (Exception)
            {
            }
            return true;
        }

        protected override void AfterStop()
        {
            _socket = null;
        }
    }
}